﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.IO;

namespace httpcluster
{
	class Program
	{
		static void Main(string[] args)
		{
			HttpListener listener = new HttpListener();
			string baseUrl = "http://+:80" + Worker.BasePath;
			listener.Prefixes.Add(baseUrl);
			listener.Start();
			Console.Title = "HTTP Cluster";
			Console.WriteLine("Listening on " + baseUrl + "...");
			int count = 0;

			if (args.Length > 0)
			{
				Worker.RedirectUrls = args;
			}

			Console.WriteLine("Redirecting to:");
			foreach (string s in Worker.RedirectUrls)
			{
				Console.WriteLine("    " + s);
			}

			for (; ; )
			{
				HttpListenerContext ctx = listener.GetContext();
				new Thread(new Worker(ctx, count++).ProcessRequest).Start();
			}
		}
	}

	class Worker
	{
		private static int _activeCount = 0;
		public static string BasePath = "/cluster/";

		public static string[] RedirectUrls = new string[]
		{
			"http://server1/",
			"http://server2/"
		};

		private HttpListenerContext _context;
		private int _count = 0;

		public Worker(HttpListenerContext context, int count)
		{
			_context = context;
			_count = count;
		}

		public void ProcessRequest()
		{
			int count = Interlocked.Increment(ref _activeCount);
			Console.WriteLine("Active: {0}", count);

			string msg = _context.Request.HttpMethod + " " + _context.Request.Url;

			string url = _context.Request.Url.PathAndQuery.Replace(BasePath, "");
			int idx = _count % RedirectUrls.Length;
			string redirectedUrl = RedirectUrls[idx] + url;
			msg += "\r\n" + idx.ToString() + ": " + redirectedUrl;
			Console.WriteLine(msg);

			HttpWebRequest req = HttpWebRequest.Create(redirectedUrl) as HttpWebRequest;

			req.Method = _context.Request.HttpMethod;
			
			HttpWebResponse resp = null;
			
			try
			{
				resp = (HttpWebResponse)req.GetResponse();
				_context.Response.ContentType = resp.ContentType;
				_context.Response.StatusCode = (int)resp.StatusCode;
				_context.Response.StatusDescription = resp.StatusDescription;
				using (Stream st = resp.GetResponseStream())
				{
					CopyStream(st, _context.Response.OutputStream);
				}
				_context.Response.OutputStream.Close();
			}
			catch (WebException exc)
			{
				Console.Error.WriteLine(exc.Message);
				HttpWebResponse exresp = exc.Response as HttpWebResponse;
				_context.Response.ContentType = exresp.ContentType;
				_context.Response.StatusCode = (int)exresp.StatusCode;
				_context.Response.StatusDescription = exresp.StatusDescription;
				using (Stream st = exresp.GetResponseStream())
				{
					CopyStream(st, _context.Response.OutputStream);
				}
				_context.Response.OutputStream.Close();
			}

			count = Interlocked.Decrement(ref _activeCount);
			Console.WriteLine("Active: {0}", count);
		}

		private static void CopyStream(Stream src, Stream dst)
		{
			byte[] bytes = new byte[4096];
			while (true)
			{
				int read = src.Read(bytes, 0, bytes.Length);
				if (read == 0)
				{
					break;
				}
				dst.Write(bytes, 0, read);
			}
		}
	}
}
